﻿using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

using NetBay.Core.BusinessEntities;

using Rio.Framework.Pages;
using Rio.Framework.Pages.AppCode.Helpers;
using Rio.Framework.Pages.AppCode.Models;
using Rio.Manager.Application.Utils;

namespace Rio.Manager.Application.ViewModel.BaseClass
{
    public abstract class TabViewModelBase : ViewModelBase, INotifyDataErrorInfo
    {
        #region Fields

        private LoadingIndicatorManager _busyIndicator = new LoadingIndicatorManager();
        private bool _canCloseTab;
        private ObservableCollection<ObservableKeyValuePair<string, ObservableCollection<string>>> _errors = new ObservableCollection<ObservableKeyValuePair<string, ObservableCollection<string>>>();
        private bool _isEdited;
        private FolderType _selectedFolderType;
        private Organization _selectedOrganization;
        private ObservableCollection<Organization> _organizations;
        private ObservableCollection<FolderType> _folderTypes;
        private Uri _tabIcon;
        private string _tabTitle;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TabViewModelBase"/> class.
        /// </summary>
        public TabViewModelBase()
        {
            CloseTabWithConfirmationCommand = new RelayCommand(CloseTabWithConfirmation);
            InitCommands();
        }

        #endregion Constructors

        #region Events

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        #endregion Events

        #region Properties

        public LoadingIndicatorManager BusyIndicator
        {
            get { return _busyIndicator; }
            set
            {
                _busyIndicator = value;

                _busyIndicator.PropertyChanged += delegate
                {
                    RaisePropertyChanged("BusyIndicator");
                };
            }
        }

        public bool CanCloseTab
        {
            get { return _canCloseTab; }
            set
            {
                _canCloseTab = value;
                RaisePropertyChanged("CanCloseTab");
            }
        }

        public RelayCommand CloseTabWithConfirmationCommand
        {
            get;
            set;
        }

        public ObservableCollection<ObservableKeyValuePair<string, ObservableCollection<string>>> Errors
        {
            get { return _errors; }
            set
            {
                _errors = value;
                RaisePropertyChanged("Errors");
                RaisePropertyChanged("HasErrors");
            }
        }

        public bool HasErrors
        {
            get { return this._errors.Count > 0; }
        }

        public bool IsEdited
        {
            get { return _isEdited; }
            set
            {
                _isEdited = value;
                RaisePropertyChanged("IsEdited");
            }
        }

        public virtual FolderType SelectedFolderType
        {
            get { return _selectedFolderType; }
            set
            {
                _selectedFolderType = value;
                RaisePropertyChanged("SelectedFolderType");
            }
        }

        public virtual Organization SelectedOrganization
        {
            get { return _selectedOrganization; }
            set
            {
                _selectedOrganization = value;
                RaisePropertyChanged("SelectedOrganization");
            }
        }

        public virtual ObservableCollection<Organization> Organizations
        {
            get { return _organizations; }
            set
            {
                _organizations = value;
                RaisePropertyChanged("Organizations");
            }
        }

        public virtual ObservableCollection<FolderType> FolderTypes
        {
            get { return _folderTypes; }
            set
            {
                _folderTypes = value;
                RaisePropertyChanged("FolderTypes");
            }
        }

        public Uri TabIcon
        {
            get { return _tabIcon; }
            set
            {
                _tabIcon = value;
                RaisePropertyChanged("TabIcon");
            }
        }

        public string TabTitle
        {
            get { return _tabTitle; }
            set
            {
                _tabTitle = value;
                RaisePropertyChanged("TabTitle");
            }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// Adds the tab with action.
        /// </summary>
        /// <param name="newTab">The new tab.</param>
        /// <param name="actionToProcess">The action to process before add tab</param>
        public void AddTab(TabViewModelBase newTab, Action<TabViewModelBase> actionToProcessBeforeAddTab = null)
        {
            Messenger.Default.Send<NotificationMessage<Tuple<TabViewModelBase, Action<TabViewModelBase>>>>(
                new NotificationMessage<Tuple<TabViewModelBase, Action<TabViewModelBase>>>(
                    new Tuple<TabViewModelBase, Action<TabViewModelBase>>(newTab, actionToProcessBeforeAddTab),
                    "add"));
        }

        /// <summary>
        /// Removes the tab.
        /// </summary>
        public void CloseTab(MessageBoxResult result = MessageBoxResult.Yes)
        {
            if (result == MessageBoxResult.Yes)
            {
                IsEdited = false;

                Messenger.Default.Send<NotificationMessage<Tuple<TabViewModelBase, Action<TabViewModelBase>>>>(
                    new NotificationMessage<Tuple<TabViewModelBase, Action<TabViewModelBase>>>(
                        new Tuple<TabViewModelBase, Action<TabViewModelBase>>(this, null),
                            "close"));
            }
        }

        /// <summary>
        /// Closes the tab with confirmation.
        /// </summary>
        public void CloseTabWithConfirmation()
        {
            //if (IsEdited)
            //{
            //    ShowModalDialog("Attention", "Des modifications ont été apportées. Êtes-vous sûr de vouloir fermer l'onglet ?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, CloseTab);
            //}
            //else
            //{
            CloseTab();
            //}
        }

        /// <summary>
        /// Gets the validation errors for a specified property or for the entire object.
        /// </summary>
        /// <param name="propertyName">The name of the property to retrieve validation errors for, or null or <see cref="F:System.String.Empty"/> to retrieve errors for the entire object.</param>
        /// <returns>
        /// The validation errors for the property or object.
        /// </returns>
        public IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName) || !this.Errors.Any(item => item.Key == propertyName))
            {
                return null;
            }

            return this.Errors.Single(item => item.Key == propertyName).Value;
        }

        /// <summary>
        /// Inits the commands.
        /// </summary>
        public abstract void InitCommands();

        /// <summary>
        /// Inits the labels.
        /// </summary>
        public abstract void InitLabels();

        /// <summary>
        /// Adds the error.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="errorMessage">The error message.</param>
        protected virtual void AddError(string propertyName, string errorMessage)
        {
            if (!this.Errors.Any(item => item.Key == propertyName))
            {
                this.Errors.Add(new ObservableKeyValuePair<string, ObservableCollection<string>>(propertyName, new ObservableCollection<string>()));
            }

            if (!this.Errors.Single(item => item.Key == propertyName).Value.Contains(errorMessage))
            {
                this.Errors.Single(item => item.Key == propertyName).Value.Add(errorMessage);
                this.OnDataErrorsChanged(propertyName);
            }

            RaisePropertyChanged("Errors");
            RaisePropertyChanged("HasErrors");
        }

        /// <summary>
        /// Called when [data errors changed].
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        protected void OnDataErrorsChanged(string propertyName)
        {
            if (this.ErrorsChanged != null)
            {
                this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Removes the error.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="errorMessage">The error message.</param>
        protected virtual void RemoveError(string propertyName, string errorMessage)
        {
            if (this.Errors.Any(item => item.Key == propertyName))
            {
                if (this.Errors.Single(item => item.Key == propertyName).Value.Contains(errorMessage))
                {
                    this.Errors.Single(item => item.Key == propertyName).Value.Remove(errorMessage);
                    if (this.Errors.Single(item => item.Key == propertyName).Value.Count == 0)
                    {
                        this.Errors.Remove(this.Errors.Single(item => item.Key == propertyName));
                    }

                    this.OnDataErrorsChanged(propertyName);
                }
            }

            RaisePropertyChanged("Errors");
            RaisePropertyChanged("HasErrors");
        }

        /// <summary>
        /// Shows a modal dialog.
        /// The associated View need to be register to receive the request.
        /// </summary>
        protected void ShowModalDialog(string dialogTitle, string dialogContent, MessageBoxButtons avalaibleButtons, MessageBoxIcon icon, Action<MessageBoxResult> callback = null)
        {
            MessageBoxChildWindow window = new MessageBoxChildWindow(
                dialogTitle,
                dialogContent,
                avalaibleButtons,
                icon);

            window.Closed += delegate
                                 {
                                     if(callback != null)
                                         callback.Invoke(window.Result);
                                 };

            window.ShowDialog();
        }

        #endregion Methods
    }
}